Skip to main content

OAuth 2.0 and OpenID Connect

Two protocols, routinely confused:

  • OAuth 2.0 is about authorisation — obtaining a token that grants scoped access to an API on behalf of a resource owner. It says nothing about who the user is.
  • OpenID Connect (OIDC) is a thin layer on top that adds authentication — an ID token asserting who the user is, and a userinfo endpoint.

If your system needs to know who logged in, you need OIDC. If it needs to call an API with delegated permission, you need OAuth. Health systems need both, plus the health-specific conventions in SMART on FHIR.


The roles​

RoleIn a health deployment
Resource ownerThe patient, or the clinician acting within their authority
ClientThe app: an EMR module, a SMART app, a CHW mobile app, a batch job
Authorisation serverKeycloak, Ory, or a national identity service
Resource serverThe FHIR server, the registry, the API gateway

The authorisation server should be a separate component from the resource server. In a single-vendor hospital deployment they are often bundled; in a national architecture identity is a shared service, and bundling it with one FHIR server means every other system either trusts that vendor or runs its own.


Grant types worth using​

Authorization code with PKCE​

The default for anything with a user. The user is redirected to the authorisation server, authenticates there, and the client exchanges a short-lived code for tokens.

PKCE (Proof Key for Code Exchange) binds the code to the client that requested it, preventing interception. It was originally for mobile apps; it is now recommended for all clients including server-side ones.

App Authorisation server Resource server
│ code_verifier (random), code_challenge = S256(verifier)
│──── /authorize?response_type=code&code_challenge=… ──▶│
│ user authenticates
│◀──────────── redirect with authorization code ────────│
│──── /token code + code_verifier ────────────────────▶│
│◀──────────── access_token, id_token, refresh_token ───│
│
│──── GET /Patient/123 Authorization: Bearer … ───────────────────▶│

Client credentials​

No user. One system authenticating as itself — a nightly export, a registry sync, an interoperability layer calling a registry.

Use asymmetric client authentication (a signed JWT assertion with a published JWKS) rather than a shared client secret. Shared secrets end up in configuration files, in version control, and in the hands of whoever last supported the integration. This is what SMART Backend Services specifies.

Token exchange (RFC 8693)​

Trading one token for another — typically when a gateway receives a user's token and needs to call a downstream service with a narrower, service-specific token that still carries the original user's identity.

Important in layered health architectures, because it preserves on behalf of whom through several hops instead of collapsing into "the gateway did it".

Grants not to use​

  • Resource owner password credentials — the app collects the user's password. Deprecated, defeats MFA and federation. It appears in health integrations constantly and should be removed.
  • Implicit — tokens in the URL fragment. Superseded by authorization code with PKCE.

Tokens​

TokenPurposeLifetimeNotes
Access tokenPresented to the APIMinutes to an hourOpaque or JWT; the resource server must validate it
ID tokenAsserts who the user isShortNever send an ID token to an API as authorisation — it is for the client
Refresh tokenObtains new access tokensLongMust be revocable; rotate on use

JWT validation at the resource server: verify the signature against the issuer's JWKS, and check iss, aud, exp, nbf and the scopes. Skipping aud validation means a token issued for one service is accepted by another — a real and common vulnerability. Cache the JWKS but honour key rotation.

Introspection versus JWT. Opaque tokens require an introspection call per request (accurate, immediately revocable, adds latency and a dependency). Self-contained JWTs avoid the call but remain valid until expiry even after revocation. For clinical data, short JWT lifetimes plus introspection on sensitive operations is the usual compromise.


Health-specific concerns​

Scopes are necessary, not sufficient​

A granted scope says what the app asked for. It does not establish a care relationship, a purpose of use, or patient consent. See identity and security for the full decision chain.

Delegation and proxy access​

A parent accessing a child's record; an adult child managing a parent's care; a guardian for a person lacking capacity. These are real, common, and legally constrained — including the point at which a minor's record becomes private from their parent, which varies by jurisdiction and by data category.

OAuth carries the delegation in the token; the authority for it comes from a relationship service or the consent service. Do not model it as "the parent has the child's login" — that is what happens in practice when the architecture does not support it, and it destroys attribution.

Federation​

In a multi-organisation ecosystem, hospitals will not surrender their staff directories. The workable pattern is identity federation: a national authorisation server that brokers to institutional identity providers, and maps the incoming identity to the health worker registry identifier.

Then trust becomes a governance question — what assurance level does each institution's authentication meet, and who verifies that? See governance.

Multi-factor authentication​

Necessary for remote access to clinical data, and a genuine problem in low-connectivity settings where SMS is unreliable and smartphones are not universal. Options include TOTP applications, hardware tokens for high-privilege accounts, and device binding for shared-device environments. Design for shared workstations — the "one login per shift" pattern destroys attribution and is what happens when individual login is too slow.

Offline access​

A CHW app may be offline for days. It cannot refresh a token. Approaches: long-lived refresh tokens held in encrypted device storage, with device-level revocation; local authentication of the user against a cached credential; and queuing signed data for later submission with its own integrity protection. Every option is a trade-off between usability and blast radius when a device is lost — decide it explicitly and write it down.


Implementation checklist​

  • Authorisation server is a distinct, independently upgradeable component
  • PKCE required for all clients
  • No password grant anywhere
  • Asymmetric client authentication for system-to-system
  • aud, iss, exp validated on every token, at every resource server
  • Scopes enforced at the API, not only issued at the token endpoint
  • Refresh tokens rotated and revocable; revocation actually tested
  • Client registration is a governed process with a named owner per client
  • Separate clients and credentials per environment
  • Every access decision, permit or deny, produces an audit event
  • Key rotation for signing keys, with a documented and rehearsed procedure
  • Session and token lifetimes chosen against a stated threat model, not copied from a tutorial

References​